package model;

import java.util.List;

import manager.IAnalizadorSintactico;
import manager.INodeData;
import manager.IToken;
import manager.ITokenStream;
import treelms.Codigo;
import treelms.Declaraciones;
import treelms.SimboloGenerico;
import utils.TreeNode;
import visitors.BuildSymbolTableVisitor;
import visitors.LoggingVisitor;
import visitors.TreeVisitor;

public class AnalizadorSintactico extends AbstractAnalizadorSintactico implements IAnalizadorSintactico {
	
	/*ATRIBUTOS*/
	private TreeNode<INodeData> nodoDeclaraciones = new TreeNode<INodeData>(new Declaraciones());	//Sintetizado
	private TreeNode<INodeData> nodoCodigo = new TreeNode<INodeData>(new Codigo());					//Sintetizado

	private List<SimboloGenerico> declaraciones; 	//Sintetizado
	private List<TreeNode<INodeData>> listaMetodos;	//Sintetizado

	
	/* INICIALIZACION */	
	public AnalizadorSintactico ( ITokenStream ts ) {
		super(ts, false);
	}
	public AnalizadorSintactico ( ITokenStream ts, boolean debugMode ) {
		super(ts,debugMode);
	}
	
	
	/**
	 * Devuelve true si reconoce toda la sarta
	 */
	public boolean reconocer () {
		
		cleanAll();
		
		boolean recognized = true; 
		
		this.setNroProduccion(PROD_ENCABEZADO);
		this.accept(LoggingVisitor.getInstance());	//Loguea encabezado
		this.accept(BuildSymbolTableVisitor.getInstance());	//Crea ambiente global

		/* <PROGRAMA> -> globales <HEADER> fin-globales; BLOQUE EOF */
		
		if ( this.getTokenSiguiente().equals( new Token(IToken.PALABRA_RESERVADA_GLOBAL) ) ) {
			
			this.setNroProduccion(PROD_PRIMERA);
			this.accept(LoggingVisitor.getInstance());	//Loguea produccion actual
			
			// Reconocer Palabra Reservada "globales"			
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_GLOBAL) ) {
				return false;
			}

			// Reconocer Header
			/*while ( Header.primeros(this.getTokenSiguiente()) ) {
				Header h = new Header(this.tokenStream, this.debugMode);
				recognized &= h.reconocer();
				
				this.declaraciones = h.getDeclaraciones();

				this.accept(BuildSymbolTableVisitor.getInstance());
				this.accept(TreeVisitor.getInstance());
				
			}*/
			// Reconocer Header
			Header h = new Header(this.tokenStream, this.debugMode);
			recognized &= h.reconocer();
			this.setValidated(h.validado());
			
			this.declaraciones = h.getDeclaraciones();

			this.accept(BuildSymbolTableVisitor.getInstance());
			this.accept(TreeVisitor.getInstance());
			
			
			if ( !this.reconocerToken(IToken.PALABRA_RESERVADA_END_GLOBAL) ) {
				return false;
			}

			//Reconocer Punto y Coma
			if ( !this.reconocerToken(IToken.TYPE_PUNTO_COMA) ) {
				return false;
			}
		}
		
		this.setNroProduccion(PROD_SEGUNDA);	//Lo preciso para saber qué agregar en el árbol		
		/*this.accept(LoggingVisitor.getInstance());	//Loguea produccion actual		
	
		if ( this.getTokenSiguiente().equals( new Token(IToken.PALABRA_RESERVADA_PROCEDURE) ) ||  
				this.getTokenSiguiente().equals( new Token(IToken.PALABRA_RESERVADA_FUNCTION) )) {

			// <G'> -> BLOQUE EOF |
					
			// Reconocer <BLOQUE>
			while ( Bloque.primeros( this.getTokenSiguiente() ) ) {
				IAnalizadorSintactico bloque = new Bloque(this.tokenStream, this.debugMode);
				recognized &= bloque.reconocer();
			}
		}
		*/
		// Reconocer Procedimientos o Funciones
				Bloque bloque = new Bloque(this.tokenStream,this.debugMode);
				
				recognized &= bloque.reconocer();
				this.setValidated(bloque.validado());

				this.listaMetodos = bloque.getListaMetodos();
				this.accept(TreeVisitor.getInstance());	//Agregar todas los métodos al nodo codigo

				this.setNroProduccion(PROD_SEGUNDA);	//Lo preciso para saber qué agregar en el árbol
				

		this.setNroProduccion(PROD_TERCERA);

		//En este punto deberia encontrar TOKEN_FIN_DE_ARCHIVO
		recognized &= this.reconocerToken(IToken.TYPE_FIN_DE_ARCHIVO);
		
		this.accept(BuildSymbolTableVisitor.getInstance());	
		this.accept(TreeVisitor.getInstance());		
		
		return recognized;
	
	}
	private void cleanAll() {
		SymbolTable.cleanAll();
		this.em.cleanAll();
		
	}
	public List<SimboloGenerico> getDeclaraciones() {
		return declaraciones;
	}
	public TreeNode<INodeData> getNodoDeclaraciones() {
		return nodoDeclaraciones;
	}
	public TreeNode<INodeData> getNodoCodigo() {
		return nodoCodigo;
	}
	public List<TreeNode<INodeData>> getListaMetodos() {
		return listaMetodos;
	}
}
